/*  Bluetooth Mesh */

/*
 * Copyright (c) 2017 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "adaptation.h"
#include "adv.h"
#include "prov.h"
#include "net.h"
#include "beacon.h"
#include "lpn.h"
#include "friend.h"
#include "transport.h"
#include "access.h"
#include "foundation.h"
#include "proxy.h"
#include "settings.h"
#include "mesh.h"

#define LOG_TAG             "[MESH-main]"
#define LOG_INFO_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_WARN_ENABLE
#define LOG_ERROR_ENABLE
#define LOG_DUMP_ENABLE
#include "mesh_log.h"

#if MESH_RAM_AND_CODE_MAP_DETAIL
#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".ble_mesh_main_bss")
#pragma data_seg(".ble_mesh_main_data")
#pragma const_seg(".ble_mesh_main_const")
#pragma code_seg(".ble_mesh_main_code")
#endif
#else /* MESH_RAM_AND_CODE_MAP_DETAIL */
#pragma bss_seg(".ble_mesh_bss")
#pragma data_seg(".ble_mesh_data")
#pragma const_seg(".ble_mesh_const")
#pragma code_seg(".ble_mesh_code")
#endif /* MESH_RAM_AND_CODE_MAP_DETAIL */

int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx,
                      u8_t flags, u32_t iv_index, u16_t addr,
                      const u8_t dev_key[16])
{
    int err;

    BT_INFO("--func=%s", __FUNCTION__);
    BT_INFO("Primary Element: 0x%04x", addr);
    BT_DBG("net_idx 0x%04x flags 0x%02x iv_index 0x%04x",
           net_idx, flags, iv_index);

    if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
        bt_mesh_proxy_prov_disable(false);
    }

    /* create 'Secure Network beacon' PDU of 'Mesh beacons' */
    err = bt_mesh_net_create(net_idx, flags, net_key, iv_index);
    if (err) {
        if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) {
            bt_mesh_proxy_prov_enable();
        }

        return err;
    }

    bt_mesh.seq = 0;

    bt_mesh_comp_provision(addr);

    memcpy(bt_mesh.dev_key, dev_key, 16);

    if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
        BT_DBG("Storing network information persistently");
        bt_mesh_store_net();
        bt_mesh_store_subnet(&bt_mesh.sub[0]);
        bt_mesh_store_iv(false);
    }

    bt_mesh_net_start();

    return 0;
}

void bt_mesh_reset(void)
{
    if (!bt_mesh.valid) {
        return;
    }

    bt_mesh.iv_index = 0;
    bt_mesh.seq = 0;
    bt_mesh.iv_update = 0;
    bt_mesh.pending_update = 0;
    bt_mesh.valid = 0;
    bt_mesh.ivu_duration = 0;
    bt_mesh.ivu_initiator = 0;

    k_delayed_work_cancel(&bt_mesh.ivu_timer);

    bt_mesh_cfg_reset();

    bt_mesh_rx_reset();
    bt_mesh_tx_reset();

    if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
        bt_mesh_lpn_disable(true);
    }

    if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
        bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY);
    }

    if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
        bt_mesh_proxy_gatt_disable();
    }

    if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
        bt_mesh_clear_net();
    }

    (void)memset(bt_mesh.dev_key, 0, sizeof(bt_mesh.dev_key));

    bt_mesh_scan_disable();
    bt_mesh_beacon_disable();

    bt_mesh_comp_unprovision();

    if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
        bt_mesh_prov_reset();
    }
}

bool bt_mesh_is_provisioned(void)
{
    return bt_mesh.valid;
}

int bt_mesh_prov_enable(bt_mesh_prov_bearer_t bearers)
{
    if (bt_mesh_is_provisioned()) {
        return -EALREADY;
    }

    if (IS_ENABLED(CONFIG_BT_DEBUG)) {
        const struct bt_mesh_prov *prov = bt_mesh_prov_get();
        struct bt_uuid_128 uuid = { .uuid.type = BT_UUID_TYPE_128 };

        memcpy(uuid.val, prov->uuid, 16);
        BT_INFO("Device UUID: %s", bt_uuid_str(&uuid.uuid));
    }

    if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) &&
        (bearers & BT_MESH_PROV_ADV)) {
        /* Make sure we're scanning for provisioning inviations */
        bt_mesh_scan_enable();
        /* Enable unprovisioned beacon sending */
        if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) {
            bt_mesh_beacon_enable();
        }
    }

    if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) &&
        (bearers & BT_MESH_PROV_GATT)) {
        bt_mesh_proxy_prov_enable();
        bt_mesh_adv_update();
    }

    return 0;
}

int bt_mesh_prov_disable(bt_mesh_prov_bearer_t bearers)
{
    if (bt_mesh_is_provisioned()) {
        return -EALREADY;
    }

    if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV) &&
        (bearers & BT_MESH_PROV_ADV)) {
        bt_mesh_beacon_disable();
        bt_mesh_scan_disable();
    }

    if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT) &&
        (bearers & BT_MESH_PROV_GATT)) {
        bt_mesh_proxy_prov_disable(true);
        bt_mesh_adv_update();
    }

    return 0;
}

int bt_mesh_init(const struct bt_mesh_prov *prov,
                 const struct bt_mesh_comp *comp)
{
    int err;

    err = bt_mesh_comp_register(comp);
    if (err) {
        return err;
    }

    if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
        err = bt_mesh_prov_init(prov);
        if (err) {
            return err;
        }
    }

    bt_mesh_net_init();
    bt_mesh_trans_init();
    bt_mesh_beacon_init();
    bt_mesh_adv_init();

    if (IS_ENABLED(CONFIG_BT_MESH_PROXY)) {
        bt_mesh_proxy_init();
    }

    if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
        bt_mesh_settings_init();
    }

    return 0;
}
